package cn.huaqingcheng.tool.core;

import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

/**
 * TreeTool
 */
@Slf4j
public abstract class TreeTool {

    /**
     * 构建 树形结构
     *
     * @param root 根节点ID 可以为空; 指定 root 可以过滤仅父ID 等于 root 的节点; 否则 返回所有无父节点的节点(即含游离结点);
     * @param list 源数据 单层列表, 如果 id 相同, 会按顺序覆盖
     * @param <I>  ID 类型 list ID 的类型
     * @param <S>  源数据类型 list 源数据的类型
     * @param map  把原数据类型 S 映射到 treeNode<I>
     * @return 树, 如果 list 为空, 返回 空集合
     */
    static <I, S, R extends TreeNode<I, R>> @NotNull List<R> build(I root, Collection<S> list, Function<? super S, ? extends R> map) {
        if (CollTool.isEmpty(list)) {
            return Collections.emptyList();
        }

        Map<I, R> nodeMap = list.parallelStream()
            .map(Objects.requireNonNull(map))
            .peek(i -> Assert.isFalse(Objects.equals(i.getId(), i.getParentId()), "非法数据: 节点 ID parentId 相同: {}", i))
            .collect(Collectors.toMap(TreeNode::getId, node -> node, (a, b) -> a));

        List<R> topNode = new ArrayList<>(10);

        for (Map.Entry<I, R> entry : nodeMap.entrySet()) {
            R node = entry.getValue();
            R pNode = nodeMap.get(node.getParentId());

            // 指定根节点模式
            if (Objects.nonNull(root)) {
                if (Objects.equals(node.getParentId(), root)) {
                    topNode.add(node);
                }
            } else {
                if (pNode == null) {
                    topNode.add(node);
                }
            }
            if (pNode == null) {
                continue;
            }

            if (Objects.isNull(pNode.getChildren())) {
                pNode.setChildren(new ArrayList<>(10));
            }
            pNode.getChildren().add(node);
        }

        return topNode;
    }

    /**
     * 遍历整个树, 对每个节点应用判断谓词, 如果判断返回 false ,标识不满足, 从树中移除此节点
     * 如果节点所有子节点都不满足条件, 移除此节点
     *
     * @param nodeList      多根节点树 list 必须是允许执行 {@link Collection#remove(Object)} 操作的实现
     * @param nodePredicate 判断谓词
     * @return 处理后的节点列表
     * @throws UnsupportedOperationException 如果 nodeList 集合不支持 <tt>remove<tt> 操作
     */
    @SuppressWarnings("unchecked") // TODO: 未检查的类型转换
    static <I, E extends TreeNode<I, E>> List<? extends E> filter(List<? extends E> nodeList, Predicate<E> nodePredicate) {
        if (CollTool.isEmpty(nodeList)) {
            return Collections.emptyList();
        }

        for (int i = 0; i < nodeList.size(); i++) {
            E node = nodeList.get(i);

            if (CollTool.isEmpty(node.getChildren())) {
                if (!nodePredicate.test(node)) {
                    log.trace("删除不符合条件的叶子节点 {}", node);
                    nodeList.remove(i);
                    i--;
                }
                continue;
            }

            List<? extends E> filter = filter(node.getChildren(), nodePredicate);
            if (CollTool.isEmpty(filter)) {
                log.trace("删除子元素为空的父节点 {}", node);
                nodeList.remove(i);
                i--;
            }

            node.setChildren((List<E>) filter);

            if (!nodePredicate.test(node)) {
                log.trace("删除不符合条件的非空父节点 {}", node);
                nodeList.remove(i);
                i--;
            }
        }

        return nodeList;
    }

    /**
     * 树结构遍历
     *
     * @param node     树节点
     * @param consumer 节点消费者
     */
    static <I, T extends TreeNode<I, T>> void each(T node, Consumer<T> consumer) {
        if (node == null) {
            return;
        }
        if (CollTool.isNotEmpty(node.getChildren())) {
            for (T child : node.getChildren()) {
                each(child, consumer);
            }
        }
        Objects.requireNonNull(consumer).accept(node);
    }

    static <I, T extends TreeNode<I, T>> void each(List<T> coll, Consumer<T> consumer) {
        if (CollTool.isEmpty(coll)) {
            return;
        }
        for (T t : coll) {
            List<T> children = t.getChildren();
            if (CollTool.isNotEmpty(children)) {
                each(children, consumer);
            }
            Objects.requireNonNull(consumer).accept(t);
        }
    }

    /**
     * 树展开
     */
    static <I, T extends TreeNode<I, T>> @NotNull List<T> expansion(@NotNull List<T> treeList) {
        List<T> endList = new ArrayList<>(treeList.size());
        each(treeList, endList::add);
        return endList;
    }

}
